/*-------------------------------------------------------------------------
 *
 * compression.c
 *
 * 压缩方法和规格的共享代码。
 *
 * 压缩规格指定在使用特定算法进行压缩时应使用的参数。
 * 最简单的压缩规格是一个整数，它设置压缩级别。
 *
 * 否则，压缩规格是一个用逗号分隔的项列表，
 * 每个项的格式为keyword或keyword=value。
 *
 * 当前支持的唯一关键字是“level”和“workers”。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *		  src/common/compression.c
 *-------------------------------------------------------------------------
 */

#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#ifdef USE_ZSTD
#include <zstd.h>
#endif
#ifdef HAVE_LIBZ
#include <zlib.h>
#endif

#include "common/compression.h"

static int	fc_expect_integer_value(char *fc_keyword, char *fc_value,
								 pg_compress_specification *fc_result);

/*
 * 根据名称查找压缩算法。如果名称被识别，则返回true并设置*algorithm，否则返回false。
 */
bool parse_compress_algorithm(char *fc_name, pg_compress_algorithm *fc_algorithm)
{
	if (strcmp(fc_name, "none") == 0)
		*fc_algorithm = PG_COMPRESSION_NONE;
	else if (strcmp(fc_name, "gzip") == 0)
		*fc_algorithm = PG_COMPRESSION_GZIP;
	else if (strcmp(fc_name, "lz4") == 0)
		*fc_algorithm = PG_COMPRESSION_LZ4;
	else if (strcmp(fc_name, "zstd") == 0)
		*fc_algorithm = PG_COMPRESSION_ZSTD;
	else
		return false;
	return true;
}

/*
 * 获取与特定压缩算法对应的人类可读名称。
 */
const char * get_compress_algorithm_name(pg_compress_algorithm fc_algorithm)
{
	switch (fc_algorithm)
	{
		case PG_COMPRESSION_NONE:
			return "none";
		case PG_COMPRESSION_GZIP:
			return "gzip";
		case PG_COMPRESSION_LZ4:
			return "lz4";
		case PG_COMPRESSION_ZSTD:
			return "zstd";
			/* 没有默认值，以便在添加值时引发编译器警告 */
	}
	Assert(false);
	return "???";				/* 安抚编译器 */
}

/*
 * 解析指定算法的压缩规范。
 *
 * 请参阅文件头注释，以获取压缩规范期望的样子简要描述。
 *
 * 返回时，结果对象的所有字段将被初始化。
 * 特别地，如果在解析过程中没有错误发生，result->parse_error将为NULL，否则将包含适当的错误信息。
 * 调用者如果需要，可以使用pfree释放此错误消息字符串。
 * 然而，请注意，即使没有解析错误，字符串可能也没有意义：例如，对于gzip，level=12是没有意义的，但它确实能够解析。
 *
 * 如果未直接在规范中指定，则默认分配压缩级别。
 *
 * 使用validate_compress_specification()来了解压缩规范是否语义上合理。
 */
void parse_compress_specification(pg_compress_algorithm fc_algorithm, char *fc_specification,
							 pg_compress_specification *fc_result)
{
	int			fc_bare_level;
	char	   *fc_bare_level_endp;

	/* 结果对象的初始设置。 */
	fc_result->algorithm = fc_algorithm;
	fc_result->options = 0;
	fc_result->parse_error = NULL;

	/*
	 * 根据压缩方法分配默认级别。这可能
	 * 稍后会被强制执行。
	 */
	switch (fc_result->algorithm)
	{
		case PG_COMPRESSION_NONE:
			fc_result->level = 0;
			break;
		case PG_COMPRESSION_LZ4:
#ifdef USE_LZ4
			fc_result->level = 0;	/* 快速压缩模式 */
#else
			fc_result->parse_error =
				psprintf(_("this build does not support compression with %s"),
						 "LZ4");
#endif
			break;
		case PG_COMPRESSION_ZSTD:
#ifdef USE_ZSTD
			fc_result->level = ZSTD_CLEVEL_DEFAULT;
#else
			fc_result->parse_error =
				psprintf(_("this build does not support compression with %s"),
						 "ZSTD");
#endif
			break;
		case PG_COMPRESSION_GZIP:
#ifdef HAVE_LIBZ
			fc_result->level = Z_DEFAULT_COMPRESSION;
#else
			fc_result->parse_error =
				psprintf(_("this build does not support compression with %s"),
						 "gzip");
#endif
			break;
	}

	/* 如果没有规范，我们已经完成了。 */
	if (fc_specification == NULL)
		return;

	/* 作为特例，规范可以是一个裸整数。 */
	fc_bare_level = strtol(fc_specification, &fc_bare_level_endp, 10);
	if (fc_specification != fc_bare_level_endp && *fc_bare_level_endp == '\0')
	{
		fc_result->level = fc_bare_level;
		return;
	}

	/* 查找以逗号分隔的关键字或关键字=值条目。 */
	while (1)
	{
		char	   *fc_kwstart;
		char	   *fc_kwend;
		char	   *fc_vstart;
		char	   *fc_vend;
		int			fc_kwlen;
		int			fc_vlen;
		bool		fc_has_value;
		char	   *fc_keyword;
		char	   *fc_value;

		/* 确定下一个关键字及其任何值的起始、结束和长度。 */
		fc_kwstart = fc_kwend = fc_specification;
		while (*fc_kwend != '\0' && *fc_kwend != ',' && *fc_kwend != '=')
			++fc_kwend;
		fc_kwlen = fc_kwend - fc_kwstart;
		if (*fc_kwend != '=')
		{
			fc_vstart = fc_vend = NULL;
			fc_vlen = 0;
			fc_has_value = false;
		}
		else
		{
			fc_vstart = fc_vend = fc_kwend + 1;
			while (*fc_vend != '\0' && *fc_vend != ',')
				++fc_vend;
			fc_vlen = fc_vend - fc_vstart;
			fc_has_value = true;
		}

		/* 拒绝空关键字。 */
		if (fc_kwlen == 0)
		{
			fc_result->parse_error =
				pstrdup(_("found empty string where a compression option was expected"));
			break;
		}

		/* 将关键字和值提取为单独的C字符串。 */
		fc_keyword = palloc(fc_kwlen + 1);
		memcpy(fc_keyword, fc_kwstart, fc_kwlen);
		fc_keyword[fc_kwlen] = '\0';
		if (!fc_has_value)
			fc_value = NULL;
		else
		{
			fc_value = palloc(fc_vlen + 1);
			memcpy(fc_value, fc_vstart, fc_vlen);
			fc_value[fc_vlen] = '\0';
		}

		/* 处理我们找到的任何关键字。 */
		if (strcmp(fc_keyword, "level") == 0)
		{
			fc_result->level = fc_expect_integer_value(fc_keyword, fc_value, fc_result);

			/*
			 * 在“options”中没有必要设置标志，
			 * 至少通过上面的逻辑已经设置了默认级别。
			 */
		}
		else if (strcmp(fc_keyword, "workers") == 0)
		{
			fc_result->workers = fc_expect_integer_value(fc_keyword, fc_value, fc_result);
			fc_result->options |= PG_COMPRESSION_OPTION_WORKERS;
		}
		else
			fc_result->parse_error =
				psprintf(_("unrecognized compression option: \"%s\""), fc_keyword);

		/* 释放内存，只是为了整洁。 */
		pfree(fc_keyword);
		if (fc_value != NULL)
			pfree(fc_value);

		/*
		 * 如果我们遇到错误或已经到达字符串的结束，则停止。
		 *
		 * 如果没有值，则关键字的结束可能是字符串的结束。
		 * 如果有值，则关键字的结束不可能是字符串的结束，
		 * 但值的结束可能是。
		 */
		if (fc_result->parse_error != NULL ||
			(fc_vend == NULL ? *fc_kwend == '\0' : *fc_vend == '\0'))
			break;

		/* 继续到下一个条目并循环。 */
		fc_specification = fc_vend == NULL ? fc_kwend + 1 : fc_vend + 1;
	}
}

/*
 * 将'value'解析为整数并返回结果。
 *
 * 如果解析失败，将result->parse_error设置为适当的消息并返回-1。
 */
static int fc_expect_integer_value(char *fc_keyword, char *fc_value, pg_compress_specification *fc_result)
{
	int			fc_ivalue;
	char	   *fc_ivalue_endp;

	if (fc_value == NULL)
	{
		fc_result->parse_error =
			psprintf(_("compression option \"%s\" requires a value"),
					 fc_keyword);
		return -1;
	}

	fc_ivalue = strtol(fc_value, &fc_ivalue_endp, 10);
	if (fc_ivalue_endp == fc_value || *fc_ivalue_endp != '\0')
	{
		fc_result->parse_error =
			psprintf(_("value for compression option \"%s\" must be an integer"),
					 fc_keyword);
		return -1;
	}
	return fc_ivalue;
}

/*
 * 如果压缩规范字符串在语法上有效且语义上合理，则返回NULL。
 * 否则，返回错误消息。
 *
 * 不测试此版本的PostgreSQL是否支持请求的压缩方法。
 */
char * validate_compress_specification(pg_compress_specification *fc_spec)
{
	int			fc_min_level = 1;
	int			fc_max_level = 1;
	int			fc_default_level = 0;

	/* 如果它甚至没有解析正确，那肯定是无效的。 */
	if (fc_spec->parse_error != NULL)
		return fc_spec->parse_error;

	/*
	 * 检查算法是否期望一个压缩级别，并且它在算法的合法范围内。
	 */
	switch (fc_spec->algorithm)
	{
		case PG_COMPRESSION_GZIP:
			fc_max_level = 9;
#ifdef HAVE_LIBZ
			fc_default_level = Z_DEFAULT_COMPRESSION;
#endif
			break;
		case PG_COMPRESSION_LZ4:
			fc_max_level = 12;
			fc_default_level = 0;	/* 快速模式 */
			break;
		case PG_COMPRESSION_ZSTD:
#ifdef USE_ZSTD
			fc_max_level = ZSTD_maxCLevel();
			fc_min_level = ZSTD_minCLevel();
			fc_default_level = ZSTD_CLEVEL_DEFAULT;
#endif
			break;
		case PG_COMPRESSION_NONE:
			if (fc_spec->level != 0)
				return psprintf(_("compression algorithm \"%s\" does not accept a compression level"),
								get_compress_algorithm_name(fc_spec->algorithm));
			break;
	}

	if ((fc_spec->level < fc_min_level || fc_spec->level > fc_max_level) &&
		fc_spec->level != fc_default_level)
		return psprintf(_("compression algorithm \"%s\" expects a compression level between %d and %d (default at %d)"),
						get_compress_algorithm_name(fc_spec->algorithm),
						fc_min_level, fc_max_level, fc_default_level);

	/*
	 * 在我们目前支持的压缩算法中，只有zstd
	 * 允许并行工作者。
	 */
	if ((fc_spec->options & PG_COMPRESSION_OPTION_WORKERS) != 0 &&
		(fc_spec->algorithm != PG_COMPRESSION_ZSTD))
	{
		return psprintf(_("compression algorithm \"%s\" does not accept a worker count"),
						get_compress_algorithm_name(fc_spec->algorithm));
	}

	return NULL;
}
